package cn.zlp.user.service.impl;

import cn.zlp.basic.exception.BusinessException;
import cn.zlp.basic.service.impl.BaseServiceImpl;
import cn.zlp.basic.util.AjaxResult;
import cn.zlp.basic.util.HttpClientUtils;
import cn.zlp.basic.util.MD5Utils;
import cn.zlp.basic.util.StrUtils;
import cn.zlp.user.constant.UserConstant;
import cn.zlp.user.constant.WxConstants;
import cn.zlp.user.domain.Logininfo;
import cn.zlp.user.domain.User;
import cn.zlp.user.domain.WxUser;
import cn.zlp.user.dto.BinderDto;
import cn.zlp.user.dto.LoginDto;
import cn.zlp.user.dto.UserDto;
import cn.zlp.user.mapper.LogininfoMapper;
import cn.zlp.user.mapper.UserMapper;
import cn.zlp.user.mapper.WxUserMapper;
import cn.zlp.user.service.ILogininfoService;
import com.alibaba.fastjson.JSONObject;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;

/**
 * 登录信息Service类,先继承公共的Service类,再实现接口
 */
@Service
public class LogininfoServiceImpl extends BaseServiceImpl<Logininfo> implements ILogininfoService {

    @Autowired
    private RedisTemplate redisTemplate;

    @Autowired(required = false)
    private LogininfoMapper logininfoMapper;

    @Autowired(required = false)
    private WxUserMapper wxUserMapper;

    @Autowired(required = false)
    private UserMapper userMapper;

    /**
     * 账号登录
     * @param loginDto
     * @return
     */
    @Override
    public AjaxResult accountLogin(LoginDto loginDto) {
        // 获取登录信息
        String username = loginDto.getUsername();
        String password = loginDto.getPassword();
        Integer loginType = loginDto.getLoginType();
        // 非空判断
        if (StringUtils.isEmpty(username)
                || StringUtils.isEmpty(password)
                || StringUtils.isEmpty(loginType)) {
            throw new BusinessException("请输入正确的登录信息");
        }
        // 根据登录信息和用户名获取登录信息
        Logininfo logininfo = logininfoMapper.findByUsername(loginDto);
        if (logininfo == null){ // 用户名错误
            throw new BusinessException("账号或密码错误!!!");
        }
        // 根据登录信息获取加密加盐后的密码
        String dbMd5Pwd = logininfo.getPassword();
        // 根据登录的密码,加上盐值生成加密加盐的密码
        String md5Pwd = MD5Utils.encrypByMd5(password + logininfo.getSalt());
        if (!dbMd5Pwd.equals(md5Pwd)){ // 密码错误
            throw new BusinessException("账号或密码错误!!!");
        }
        if (!logininfo.getDisable()){ // 登录信息被禁用
            throw new BusinessException("你的账号已被冻结,请联系管理员!!!");
        }
        // 用UUID随机生成一串字符串,作为jsessionid
        String token = UUID.randomUUID().toString();
        // 将token和登录信息logininfo保存到redis数据库中,货期时间设置为30分钟
        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
        // 声明一个map对象用来装数据
        Map<String,Object> map = new HashMap<>();
        // 在map中保存token,用来登录后返回到前端,让背后进行保存到游览器中
        map.put("token",token);
        // 将登录信息取出密码和盐值,使得更加安全
        logininfo.setPassword(null);
        logininfo.setSalt(null);
        map.put("logininfo",logininfo);
        return AjaxResult.me().setResultObj(map);
    }

    /**
     * 微信三方登录
     * @param map
     * @return
     */
    @Override
    public AjaxResult wxUserLogin(Map<String, String> map) {
        // 获取前端传过来的code
        String code = map.get("code");
        // 将公司三方认证常量和code替换到请求常量中,通过授权码code向第三方获取令牌
        String url = WxConstants.GET_ACK_URL.replace("APPID", WxConstants.APPID)
                .replace("SECRET", WxConstants.SECRET)
                .replace("CODE", code);
        // 通过HttpClientUtils发送请求,返回为json字符串
        String jsonStr  = HttpClientUtils.httpGet(url);
        // {"access_token":"kXIgRn6umaxMJ6s","refresh_token":"51_6jqH7tlVAxhrYGM","unionid":"ovnCWwyyvxhj0AYsWw","openid":"oa9wA0Tu0w","scope":"snsapi_login","expires_in":7200}
        // 通过阿里巴巴提供的JSONObject,将json字符串转换为json对象
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        // 从json对象中获取access_token令牌
        String access_token = jsonObject.getString("access_token");
        // 从json对象中获取openid微信账号
        String openid = jsonObject.getString("openid");
        // 根据微信账号查询微信用户
        WxUser wxUser = wxUserMapper.findByOpendId(openid);
        if (wxUser != null && wxUser.getUser_id() != null){ // 微信账户存在,且关联上了用户,说明直接免密登录
            // 生成随机token
            String token = UUID.randomUUID().toString();
            // 获取用户id
            Long user_id = wxUser.getUser_id();
            // 根据用户id查询用户登录信息
            Logininfo logininfo = logininfoMapper.findByUserId(user_id);
            // 将token和登录信息保存到redis数据库中,设置过期时间30分钟
            redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
            // 声明一个map对象用来装数据
            Map<String,Object> wxmap = new HashMap<>();
            // 在map中保存token,用来登录后返回到前端,让背后进行保存到游览器中
            wxmap.put("token",token);
            // 将登录信息取出密码和盐值,使得更加安全
            logininfo.setPassword(null);
            logininfo.setSalt(null);
            wxmap.put("logininfo",logininfo);
            return AjaxResult.me().setResultObj(wxmap);
        } else { // 没有微信用户或者没有绑定用户id
            Map<String,Object> wxmap = new HashMap<>();
            // 将令牌和微信账号放到wxmap集合中,然后通过AjaxResult中ResultObj返回
            wxmap.put("access_token",access_token);
            wxmap.put("openid",openid);
            return AjaxResult.me().setSuccess(false).setResultObj(wxmap);
        }
    }

    /**
     * 微信绑定注册
     * @param binderDto
     */
    @Override
    @Transactional
    public AjaxResult toBinder(BinderDto binderDto) {
        // 获取注册信息
        String phone = binderDto.getPhone();
        String code = binderDto.getCode();
        String accessToken = binderDto.getAccessToken();
        String openId = binderDto.getOpenId();
        // 判断注册信息是否为空,一个为空都不执行
        if (StringUtils.isEmpty(phone)
                || StringUtils.isEmpty(code)
        ) {
            throw new BusinessException("请输入正确的信息,信息不能为空!!!");
        }
        // 获取redis数据库中的该号码微信绑定的验证码
        Object obj = redisTemplate.opsForValue().get(UserConstant.BINDER_CODE_PREFIX + phone);
        if (obj == null){
            throw new BusinessException("验证码无效,请重新获取最新验证码!!!");
        }
        // 获取验证码
        String code1 = obj.toString().split(":")[0];
        if (!code.equalsIgnoreCase(code1)){ // 忽略大小写进行比较
            throw new BusinessException("验证码错误,请输入正确的验证码!!!");
        }
        // 将令牌和微信账号替换到请求常量中,通过授权码令牌向第三方获取用户资源
        String url = WxConstants.GET_USER_URL.replace("ACCESS_TOKEN",accessToken)
                .replace("OPENID",openId);
        // 通过HttpClientUtils发送请求,返回为json字符串
        String wxUserStr = HttpClientUtils.httpGet(url);
        // 调用wxUserStr2WxUser方法,将微信用户json字符串转换为微信用户
        WxUser wxUser =  wxUserStr2WxUser(wxUserStr);
        // 根据电话查询用户
        User user = userMapper.findByPhone(phone);
        if (user == null){ // 说明没有绑定用户信息
            // 通过phone电话转换为用户对象
            user = phone2User(phone);
            // 调用user2Logininfo方法将用户对象转换为登录信息对象
            Logininfo logininfo = user2Logininfo(user);
            // 添加登录信息
            logininfoMapper.save(logininfo);
            // 将登录信息的自增长主键赋值给登录信息id
            user.setLogininfo_id(logininfo.getId());
            // 添加用户
            userMapper.save(user);
        }
        // 将用户id赋值给微信用户的关联属性User_id
        wxUser.setUser_id(user.getId());
        // 添加微信用户
        wxUserMapper.save(wxUser);
        // 免密登录,生成随机token
        String token = UUID.randomUUID().toString();
        // 根据登录信息id查询登录信息
        Logininfo logininfo = logininfoMapper.loadById(user.getLogininfo_id());
        // 将token和登录信息保存到redis数据库中,设置过期时间30分钟
        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
        // 声明一个map对象用来保存数据
        Map<String,Object> wxmap = new HashMap<>();
        // 在map中保存token,用来登录后返回到前端,让背后进行保存到游览器中
        wxmap.put("token",token);
        // 将登录信息取出密码和盐值,使得更加安全
        logininfo.setPassword(null);
        logininfo.setSalt(null);
        wxmap.put("logininfo",logininfo);
        return AjaxResult.me().setResultObj(wxmap);
    }

    @Override
    public AjaxResult verificationLogin(UserDto userDto) {
        String phone = userDto.getPhone();
        String code = userDto.getCode();
        // 判断注册信息是否为空,一个为空都不执行
        if (StringUtils.isEmpty(phone)
                || StringUtils.isEmpty(code)
        ) {
            throw new BusinessException("请输入正确的信息,信息不能为空!!!");
        }
        User user = userMapper.findByPhone(phone);
        if (user == null) {
            throw new BusinessException("请输入正确的手机号!!!");
        }
        // 获取redis数据库中的该号码手机注册的验证码
        Object obj = redisTemplate.opsForValue().get(UserConstant.LOGIN_CODE_PREFIX + phone);
        if (obj == null){
            throw new BusinessException("验证码无效,请重新获取最新验证码!!!");
        }
        // 获取验证码
        String code1 = obj.toString().split(":")[0];
        if (!code.equalsIgnoreCase(code1)){ // 忽略大小写进行比较
            throw new BusinessException("验证码错误,请输入正确的验证码!!!");
        }
        // 免密登录,生成随机token
        String token = UUID.randomUUID().toString();
        // 根据登录信息id查询登录信息
        Logininfo logininfo = logininfoMapper.loadById(user.getLogininfo_id());
        // 将token和登录信息保存到redis数据库中,设置过期时间30分钟
        redisTemplate.opsForValue().set(token,logininfo,30, TimeUnit.MINUTES);
        // 声明一个map对象用来保存数据
        Map<String,Object> vcmap = new HashMap<>();
        // 在map中保存token,用来登录后返回到前端,让背后进行保存到游览器中
        vcmap.put("token",token);
        // 将登录信息取出密码和盐值,使得更加安全
        logininfo.setPassword(null);
        logininfo.setSalt(null);
        vcmap.put("logininfo",logininfo);
        return AjaxResult.me().setResultObj(vcmap);
    }

    /**
     * 微信用户json字符串转换为微信用户方法
     * @param wxUserStr
     * @return
     */
    private WxUser wxUserStr2WxUser(String wxUserStr) {
        // 创建一个微信用户对象
        WxUser wxUser = new WxUser();
        // 将json字符串转换为json对象
        JSONObject jsonObject = JSONObject.parseObject(wxUserStr);
        // 根据key值获取json对象中的value值,然后保存在微信用户中
        wxUser.setOpenid(jsonObject.getString("openid"));
        wxUser.setNickname(jsonObject.getString("nickname"));
        wxUser.setSex(jsonObject.getInteger("sex"));
        String address = jsonObject.getString("country") + jsonObject.getString("province") + jsonObject.getString("city");
        System.out.println(jsonObject.getString("country"));
        System.out.println(jsonObject.getString("province"));
        System.out.println(jsonObject.getString("city"));
        wxUser.setAddress(address);
        wxUser.setHeadimgurl(jsonObject.getString("headimgurl"));
        wxUser.setUnionid(jsonObject.getString("unionid"));
        return wxUser;
    }

    /**
     * 电话转换为用户对象
     * @param phone
     * @return
     */
    private User phone2User(String phone) {
        // 新建一个user对象
        User user = new User();
        // 设置电话为用户名
        user.setUsername(phone);
        // 设置电话
        user.setPhone(phone);
        // 生成32为随机盐值
        String salt = StrUtils.getComplexRandomString(32);
        // 生成随机6为密码,然后加密加盐,是否为随机密码这根据业务确定
        String password = MD5Utils.encrypByMd5(StrUtils.getComplexRandomString(6) + salt);
        // 设置盐值和密码
        user.setSalt(salt);
        user.setPassword(password);
        return user;
    }

    /**
     * 用户转换为登录信息对象
     * @param user
     * @return
     */
    private Logininfo user2Logininfo(User user) {
        // 新建一个登录信息对象
        Logininfo logininfo = new Logininfo();
        // 用BeanUtils的copyProperties将user中相同属性复制到logininfo中,因为user时新建的,所以也没有id,所以不用考虑登录信息中id的问题
        BeanUtils.copyProperties(user,logininfo);
        // 设置登录信息中的类型,1为用户
        logininfo.setType(1);
        return logininfo;
    }


}
